#pragma once
#include "Socket.hpp"
#include "Util.hpp"
#include "Log.hpp"
#include "TcpServer.hpp"
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>

using namespace LogModule;
using namespace SocketModule;

const std::string gspace = " ";
const std::string glinespace = "\r\n";
const std::string glinesep = ": ";

const std::string webroot = "./wwwroot";
const std::string homepage = "/index.html";
const std::string page_404 = "/404.html";

class HttpRequest
{
public:
    HttpRequest():_is_interact(false)
    {}

    std::string Serialize()
    {
        //网页的请求已经是序列化的了，不需要我们手动实现
        return std::string();
    }

    void ParseReqLine(std::string& reqline)
    {
        //GET / HTTP/1.1
        std::stringstream ss(reqline);
        ss >> _method >> _uri >> _version;
    }

    bool Deserialize(std::string& reqstr)
    {
        //1.提取请求行
        std::string reqline;
        bool res = Util::ReadOneLine(reqstr,&reqline,glinespace);
        LOG(LogLevel::DEBUG) << reqline;

        //2.对请求进行反序列化
        ParseReqLine(reqline);
        if(_uri == "/")
            _uri = webroot + homepage;
        else 
            _uri = webroot + _uri;
        LOG(LogLevel::DEBUG) << "_method: " << _method;
        LOG(LogLevel::DEBUG) << "_uri: " << _uri;
        LOG(LogLevel::DEBUG) << "_version: " << _version;

        //对于GET方法，参数是包含在uri中的
        //例如：http://1.94.163.183:8080/login?email=hhh%40qq.com&password=123
        const std::string temp = "?";
        auto pos = _uri.find(temp);
        if(pos == std::string::npos)
        {
            //不含参的uri
            return true;
        }
        
        _args = _uri.substr(pos+temp.size());
        _uri = _uri.substr(0,pos);
        _is_interact = true;

        return true;
    }

    std::string Uri(){return _uri;}
    bool isInteract() { return _is_interact; }
    std::string Args() {return _args; }

    ~HttpRequest(){}
private:
    std::string _method;
    std::string _uri;
    std::string _version;

    std::unordered_map<std::string,std::string> _headers;
    std::string _blankline;
    std::string _text;

    bool _is_interact;//是否有交互
    std::string _args;
};

class HttpResponse
{
public:
    HttpResponse():_blankline(glinespace),_version("HTTP/1.0")
    {}

    //成熟的HTTP应答做序列化是不需要依赖任何第三方库的
    std::string Serialize()
    {
        std::string status_line = _version + gspace + std::to_string(_code) + gspace + _desc + glinespace;
        std::string resq_header;
        for(auto& header:_headers)
        {
            resq_header += (header.first + glinesep + header.second + glinespace);
        }
        return status_line + resq_header + _blankline + _text;
    }

    void SetTargetFile(const std::string& target)
    {
        _targetfile = target;
    }

    void SetCode(int code)
    {
        _code = code;
        switch (_code)
        {
        case 200:
            _desc = "OK";
            break;
        case 404:
            _desc = "Not Found";
            break;
        case 301:
            _desc = "Moved Permanently";
            break;
        case 302:
            _desc = "See Other";
            break;
        default:
            break;
        }
    }

    void SetHeader(const std::string& key,const std::string& value)
    {
        auto iter = _headers.find(key);
        if(iter != _headers.end())
            return;
        _headers.insert(std::make_pair(key,value));
    }

    std::string Uri2Suffix(const std::string &targetfile)
    {
        // ./wwwroot/a/b/c.html
        auto pos = targetfile.rfind(".");
        if (pos == std::string::npos)
        {
            return "text/html";
        }

        std::string suffix = targetfile.substr(pos);
        if (suffix == ".html" || suffix == ".htm")
            return "text/html";
        else if (suffix == ".jpg")
            return "image/jpeg";
        else if (suffix == "png")
            return "image/png";
        else
            return "";
    }

    bool MakeResPonse()
    {
        if (_targetfile == "./wwwroot/favicon.ico")
        {
            LOG(LogLevel::DEBUG) << "用户请求: " << _targetfile << "忽略它";
            return false;
        }
        if (_targetfile == "./wwwroot/redir_test")
        {
            SetCode(301);
            SetHeader("Location", "https://www.qq.com/");
            return true;
        }

        int filesize = 0;
        bool res = Util::ReadFileContent(_targetfile,&_text);
        if(!res)
        {
            _text = "";
            SetCode(404);
            _targetfile = webroot + page_404;
            filesize = Util::FileSize(_targetfile);
            Util::ReadFileContent(_targetfile,&_text);
            std::string suffix = Uri2Suffix(_targetfile);
            SetHeader("Content-Type",suffix);
            SetHeader("Content-Length",std::to_string(filesize));
        }
        else
        {
            SetCode(200);
            filesize = Util::FileSize(_targetfile);
            std::string suffix = Uri2Suffix(_targetfile);
            SetHeader("Conent-Type", suffix);
            SetHeader("Content-Length", std::to_string(filesize));
            //SetHeader("Set-Cookie","username=zhangsan");
        }
        return true;
    }

    void SetText(const std::string &t)
    {
        _text = t;
    }

    bool Deserialize(std::string& reqstr)
    {
        return true;
    }

    ~HttpResponse(){}
public:
    std::string _version;
    int _code;
    std::string _desc;

    std::unordered_map<std::string,std::string> _headers;
    std::string _blankline;
    std::string _text;

    //其他属性
    std::string _targetfile;
};

using http_func_t = std::function<void(HttpRequest& req,HttpResponse& resp)>; 
//提供动态交互的能力

class Http
{
private:
    std::string GetMonthName(int month)
    {
        std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return months[month];
    }
    std::string GetWeekDayName(int day)
    {
        std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return weekdays[day];
    }
    std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间
    {
        time_t timeout = time(nullptr) + t;
        struct tm *tm = gmtime(&timeout); // 这里不能用localtime，因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间
        char timebuffer[1024];
        //时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTC
        snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC", 
            GetWeekDayName(tm->tm_wday).c_str(),
            tm->tm_mday,
            GetMonthName(tm->tm_mon).c_str(),
            tm->tm_year+1900,
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec
        );
        return timebuffer;
    }
public:
    Http(uint16_t port):tsvrp(std::make_unique<TcpServer>(port))
    {}

    std::string ProveCookieWrite() // 证明cookie能被写入浏览器
    {
        return "Set-Cookie: username=zhangsan;";
    }
    std::string ProveCookieTimeOut()
    {
        return "Set-Cookie: username=zhangsan; expires=" + ExpireTimeUseRfc1123(60) + ";"; // 让cookie 1min后过期
    }
    std::string ProvePath()
    {
        return "Set-Cookie: username=zhangsan; path=/a/b;";
    }
    std::string ProveOtherCookie()
    {
        return "Set-Cookie: passwd=1234567890; path=/a/b;";
    }

    //注册服务
    void RegisterService(const std::string name,http_func_t h)
    {
        std::string key = webroot + name;// ./wwwroot/login
        auto iter = _route.find(key);
        if(iter == _route.end())
        {
            _route.insert(std::make_pair(key,h));
        }
    }

    void HandlerHttpRequest(std::shared_ptr<Socket> &sock, InetAddr &client)
    {
        std::string httpreqstr;
        int n = sock->Recv(&httpreqstr);
        if(n > 0)
        {
            //这里没有对报文完整性做检查
            HttpRequest req;
            HttpResponse resp;
            req.Deserialize(httpreqstr);
            if(req.isInteract())//是否是动态互动
            {
                if (_route.find(req.Uri()) == _route.end())
                {
                    // SetCode(302)
                }
                else
                {
                    _route[req.Uri()](req, resp);
                    std::string response_str = resp.Serialize();
                    sock->Send(response_str);
                }
            }
            else//到这里就只是请求静态资源
            {
                resp.SetTargetFile(req.Uri());
                if(resp.MakeResPonse())
                {
                    sock->Send(resp.Serialize());
                }
            }

            // HttpRequest req;
            // req.Deserialize(httpreqstr);
            // HttpResponse resp;
            // resp.SetTargetFile(req.Uri());
            // if(resp.MakeResPonse())
            // {
            //     std::string response_str = resp.Serialize();
            //     sock->Send(response_str);
            //     std::cout << "response_ptr:" <<response_str << std::endl;
            // }
            // else
            // {
            //     resp.SetTargetFile("./wwwroot/404.html");
            //     resp.MakeResPonse();
            //     std::string response_str = resp.Serialize();
            //     sock->Send(response_str);
            //     std::cout << "response_ptr:" <<response_str << std::endl;
            // }

            // std::cout << httpreqstr;
            // HttpResponse resp;
            // resp._version = "HTTP/1.1";
            // resp._code = 200; // success
            // resp._desc = "OK";

            // std::string filename = webroot + "/" + homepage; // "./wwwroot/index.html";
            // bool res = Util::ReadFileContent(filename, &(resp._text));
            // ///std::cout << "resp._text:" << res << ":"<< resp._text << std::endl;
            // std::string response_str = resp.Serialize();
            // sock->Send(response_str);
            //std::cout << "response_str:"<<response_str<<std::endl;
        }
    }

    void Start()
    {
        tsvrp->Start([this](std::shared_ptr<Socket> sock, InetAddr &client)
                     { this->HandlerHttpRequest(sock, client); });
    }

    ~Http(){}

private:
    std::unique_ptr<TcpServer> tsvrp;
    std::unordered_map<std::string,http_func_t> _route;
};